<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  <meta http-equiv="X-UA-Compatible" content="IE=edge" >
  <title>ReactiveCocoa之基础篇 | Grx Blog</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="前言很多blog都说ReactiveCocoa好用，然后各种秀自己如何灵活运用ReactiveCocoa，但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章，小编看了很多篇都没看出怎么使用ReactiveCocoa,于是决定自己写一遍关于学习ReactiveCocoa的文章,本文主要针对如何从零开始学习ReactiveCocoa，这里非常感谢3个人（支点的雷纯锋，camera360的">
<meta property="og:type" content="article">
<meta property="og:title" content="ReactiveCocoa之基础篇">
<meta property="og:url" content="http://yoursite.com/2015/05/04/ReactiveCocoa之基础篇/index.html">
<meta property="og:site_name" content="Grx Blog">
<meta property="og:description" content="前言很多blog都说ReactiveCocoa好用，然后各种秀自己如何灵活运用ReactiveCocoa，但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章，小编看了很多篇都没看出怎么使用ReactiveCocoa,于是决定自己写一遍关于学习ReactiveCocoa的文章,本文主要针对如何从零开始学习ReactiveCocoa，这里非常感谢3个人（支点的雷纯锋，camera360的">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-c102cdc07cd7e99e.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-a2fa6abca6338900.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-7e20e5b34f035fd2.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-fe6eda7194ebbbfb.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-b4c74ff27e4da97b.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-1c9dc45eadd64c75.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-1554573c7ad8321f.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-e684a824ac51a74c.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-311191366a0b0972.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:image" content="http://upload-images.jianshu.io/upload_images/304825-1e8effc941c8498a.png?imageMogr2/auto-orient/strip%7CimageView2/2">
<meta property="og:updated_time" content="2016-07-13T08:57:36.000Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="ReactiveCocoa之基础篇">
<meta name="twitter:description" content="前言很多blog都说ReactiveCocoa好用，然后各种秀自己如何灵活运用ReactiveCocoa，但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章，小编看了很多篇都没看出怎么使用ReactiveCocoa,于是决定自己写一遍关于学习ReactiveCocoa的文章,本文主要针对如何从零开始学习ReactiveCocoa，这里非常感谢3个人（支点的雷纯锋，camera360的">
<meta name="twitter:image" content="http://upload-images.jianshu.io/upload_images/304825-c102cdc07cd7e99e.png?imageMogr2/auto-orient/strip%7CimageView2/2">
  
    <link rel="alternative" href="/atom.xml" title="Grx Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  <link rel="stylesheet" href="/css/style.css">
</head>

<body>
  <div id="container">
    <div class="left-col">
    <div class="overlay"></div>
<div class="intrude-less">
	<header id="header" class="inner">
		<a href="/" class="profilepic">
			
			<img lazy-src="/img/rufei.jpg" class="js-avatar">
			
		</a>

		<hgroup>
		  <h1 class="header-author"><a href="/">Grx</a></h1>
		</hgroup>

		
		<p class="header-subtitle">自我管理，知识管理，时间管理，阅读，语音写作，思维导图</p>
		

		
			<div class="switch-btn">
				<div class="icon">
					<div class="icon-ctn">
						<div class="icon-wrap icon-house" data-idx="0">
							<div class="birdhouse"></div>
							<div class="birdhouse_holes"></div>
						</div>
						<div class="icon-wrap icon-ribbon hide" data-idx="1">
							<div class="ribbon"></div>
						</div>
						
						<div class="icon-wrap icon-link hide" data-idx="2">
							<div class="loopback_l"></div>
							<div class="loopback_r"></div>
						</div>
						
						
						<div class="icon-wrap icon-me hide" data-idx="3">
							<div class="user"></div>
							<div class="shoulder"></div>
						</div>
						
					</div>
					
				</div>
				<div class="tips-box hide">
					<div class="tips-arrow"></div>
					<ul class="tips-inner">
						<li>菜单</li>
						<li>标签</li>
						
						<li>友情链接</li>
						
						
						<li>关于我</li>
						
					</ul>
				</div>
			</div>
		

		<div class="switch-area">
			<div class="switch-wrap">
				<section class="switch-part switch-part1">
					<nav class="header-menu">
						<ul>
						
							<li><a href="/">主页</a></li>
				        
							<li><a href="/tags/life">随笔</a></li>
				        
							<li><a href="/archives">所有文章</a></li>
				        
						</ul>
					</nav>
					<nav class="header-nav">
						<div class="social">
							
								<a class="github" target="_blank" href="https://github.com/ruixiaoguo" title="github">github</a>
					        
								<a class="weibo" target="_blank" href="https://weibo.com/u/3984935859?refer_flag=1005055010_" title="weibo">weibo</a>
					        
								<a class="rss" target="_blank" href="#" title="rss">rss</a>
					        
								<a class="zhihu" target="_blank" href="#" title="zhihu">zhihu</a>
					        
						</div>
					</nav>
				</section>
				
				
				<section class="switch-part switch-part2">
					<div class="widget tagcloud" id="js-tagcloud">
						<a href="/tags/OC/" style="font-size: 10px;">-OC</a> <a href="/tags/Android/" style="font-size: 10px;">Android</a> <a href="/tags/Apple-Watch/" style="font-size: 10px;">Apple Watch</a> <a href="/tags/Flutter/" style="font-size: 13.33px;">Flutter</a> <a href="/tags/Life/" style="font-size: 10px;">Life</a> <a href="/tags/Lottie/" style="font-size: 10px;">Lottie</a> <a href="/tags/MMKV/" style="font-size: 10px;">MMKV</a> <a href="/tags/MonkeyKing/" style="font-size: 10px;">MonkeyKing</a> <a href="/tags/OC/" style="font-size: 20px;">OC</a> <a href="/tags/RAC/" style="font-size: 11.67px;">RAC</a> <a href="/tags/Swift/" style="font-size: 15px;">Swift</a> <a href="/tags/WKWebView/" style="font-size: 10px;">WKWebView</a> <a href="/tags/WebView/" style="font-size: 10px;">WebView</a> <a href="/tags/Xcode/" style="font-size: 18.33px;">Xcode</a> <a href="/tags/appledoc/" style="font-size: 10px;">appledoc</a> <a href="/tags/hexo/" style="font-size: 11.67px;">hexo</a> <a href="/tags/ios/" style="font-size: 10px;">ios</a> <a href="/tags/life/" style="font-size: 16.67px;">life</a> <a href="/tags/xcode/" style="font-size: 10px;">xcode</a>
					</div>
				</section>
				
				
				
				<section class="switch-part switch-part3">
					<div id="js-friends">
					
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.devtang.com">唐巧的博客</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://beyondvincent.com">破船之家</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://limboy.me">无网不剩</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://msching.github.io">码农人生</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://yulingtianxia.com">玉令天下</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.leichunfeng.com">雷纯锋的技术博客</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://jax.cnblogs.com">包建强</a>
			        
			          <a target="_blank" class="main-nav-link switch-friends-link" href="http://blog.cnbang.net">bang</a>
			        
			        </div>
				</section>
				

				
				
				<section class="switch-part switch-part4">
				
					<div id="js-aboutme">知识管理，时间管理，自我管理，阅读，语音写作，思维导图，Wiz，TimeMeter</div>
				</section>
				
			</div>
		</div>
	</header>				
</div>

    </div>
    <div class="mid-col">
      <nav id="mobile-nav">
  	<div class="overlay">
  		<div class="slider-trigger"></div>
  		<h1 class="header-author js-mobile-header hide">Grx</h1>
  	</div>
	<div class="intrude-less">
		<header id="header" class="inner">
			<div class="profilepic">
			
				<img lazy-src="/img/rufei.jpg" class="js-avatar">
			
			</div>
			<hgroup>
			  <h1 class="header-author">Grx</h1>
			</hgroup>
			
			<p class="header-subtitle">自我管理，知识管理，时间管理，阅读，语音写作，思维导图</p>
			
			<nav class="header-menu">
				<ul>
				
					<li><a href="/">主页</a></li>
		        
					<li><a href="/tags/life">随笔</a></li>
		        
					<li><a href="/archives">所有文章</a></li>
		        
		        <div class="clearfix"></div>
				</ul>
			</nav>
			<nav class="header-nav">
				<div class="social">
					
						<a class="github" target="_blank" href="https://github.com/ruixiaoguo" title="github">github</a>
			        
						<a class="weibo" target="_blank" href="https://weibo.com/u/3984935859?refer_flag=1005055010_" title="weibo">weibo</a>
			        
						<a class="rss" target="_blank" href="#" title="rss">rss</a>
			        
						<a class="zhihu" target="_blank" href="#" title="zhihu">zhihu</a>
			        
				</div>
			</nav>
		</header>				
	</div>
</nav>

      <div class="body-wrap"><article id="post-ReactiveCocoa之基础篇" class="article article-type-post" itemscope itemprop="blogPost">
  
    <div class="article-meta">
      <a href="/2015/05/04/ReactiveCocoa之基础篇/" class="article-date">
  	<time datetime="2015-05-04T06:07:11.000Z" itemprop="datePublished">2015-05-04</time>
</a>
    </div>
  
  <div class="article-inner">
    
      <input type="hidden" class="isFancy" />
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      ReactiveCocoa之基础篇
    </h1>
  

      </header>
      
      <div class="article-info article-info-post">
        
	<div class="article-tag tagcloud">
		<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/RAC/">RAC</a></li></ul>
	</div>

        

        <div class="clearfix"></div>
      </div>
      
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>很多blog都说ReactiveCocoa好用，然后各种秀自己如何灵活运用ReactiveCocoa，但是感觉真正缺少的是一篇如何学习ReactiveCocoa的文章，小编看了很多篇都没看出怎么使用ReactiveCocoa,于是决定自己写一遍关于学习ReactiveCocoa的文章,本文主要针对如何从零开始学习ReactiveCocoa，这里非常感谢3个人（支点的雷纯锋，camera360的宋潘，以及我的小学弟何宗柱（我爱科技）），在我研究ReactiveCocoa对我的帮助。<br><a id="more"></a></p>
<p>如果喜欢我的文章，可以关注我，也可以来小码哥，了解下我们的iOS培训课程。之后还会更新《最快让你上手ReactiveCocoa之进阶篇》</p>
<p>文／峥吖（简书作者）<br>原文链接：<a href="http://www.jianshu.com/p/87ef6720a096" target="_blank" rel="external">http://www.jianshu.com/p/87ef6720a096</a><br>著作权归作者所有，转载请联系作者获得授权，并标注“简书作者”。</p>
<h2 id="1-ReactiveCocoa简介"><a href="#1-ReactiveCocoa简介" class="headerlink" title="1.ReactiveCocoa简介"></a>1.ReactiveCocoa简介</h2><p>ReactiveCocoa（简称为RAC）,是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称，因此很多苹果框架喜欢以Cocoa结尾。</p>
<h2 id="2-ReactiveCocoa作用"><a href="#2-ReactiveCocoa作用" class="headerlink" title="2.ReactiveCocoa作用"></a>2.ReactiveCocoa作用</h2><ul>
<li>在我们iOS开发过程中，当某些事件响应的时候，需要处理某些业务逻辑,这些事件都用不同的方式来处理。</li>
<li>比如按钮的点击使用action，ScrollView滚动使用delegate，属性值改变使用KVO等系统提供的方式。</li>
<li>其实这些事件，都可以通过RAC处理</li>
<li>ReactiveCocoa为事件提供了很多处理方法，而且利用RAC处理事件很方便，可以把要处理的事情，和监听的事情的代码放在一起，这样非常方便我们管理，就不需要跳到对应的方法里。非常符合我们开发中高聚合，低耦合的思想。</li>
</ul>
<h2 id="3-编程思想"><a href="#3-编程思想" class="headerlink" title="3.编程思想"></a>3.编程思想</h2><p>在开发中我们也不能太依赖于某个框架，否则这个框架不更新了，导致项目后期没办法维护，比如之前Facebook提供的Three20框架，在当时也是神器，但是后来不更新了，也就没什么人用了。因此我感觉学习一个框架，还是有必要了解它的编程思想。</p>
<p>先简单介绍下目前咱们已知的编程思想。</p>
<p>文／峥吖（简书作者）<br>原文链接：<a href="http://www.jianshu.com/p/87ef6720a096" target="_blank" rel="external">http://www.jianshu.com/p/87ef6720a096</a><br>著作权归作者所有，转载请联系作者获得授权，并标注“简书作者”。</p>
<p>3.1 面向过程：处理事情以过程为核心，一步一步的实现。</p>
<p>3.2 面向对象：万物皆对象</p>
<p>3.3 链式编程思想：是将多个操作（多行代码）通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)</p>
<ul>
<li>链式编程特点：方法的返回值是block,block必须有返回值（本身对象），block参数（需要操作的值）</li>
<li>代表：masonry框架。</li>
<li>模仿masonry，写一个加法计算器，练习链式编程思想。</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-c102cdc07cd7e99e.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-a2fa6abca6338900.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-7e20e5b34f035fd2.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-fe6eda7194ebbbfb.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-b4c74ff27e4da97b.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p>3.4 响应式编程思想：不需要考虑调用顺序，只需要知道考虑结果，类似于蝴蝶效应，产生一个事件，会影响很多东西，这些事件像流一样的传播出去，然后影响结果，借用面向对象的一句话，万物皆是流。</p>
<ul>
<li>代表：KVO运用。</li>
</ul>
<p>3.5 函数式编程思想：是把操作尽量写成一系列嵌套的函数或者方法调用。</p>
<ul>
<li>函数式编程特点：每个方法必须有返回值（本身对象）,把函数或者Block当做参数,block参数（需要操作的值）block返回值（操作结果）</li>
<li>代表：ReactiveCocoa。</li>
<li>用函数式编程实现，写一个加法计算器,并且加法计算器自带判断是否等于某个值.</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-1c9dc45eadd64c75.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-1554573c7ad8321f.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<h2 id="4-ReactiveCocoa编程思想"><a href="#4-ReactiveCocoa编程思想" class="headerlink" title="4.ReactiveCocoa编程思想"></a>4.ReactiveCocoa编程思想</h2><p>ReactiveCocoa结合了几种编程风格：</p>
<p>函数式编程（Functional Programming）</p>
<p>响应式编程（Reactive Programming）</p>
<p>所以，你可能听说过ReactiveCocoa被描述为函数响应式编程（FRP）框架。</p>
<p>以后使用RAC解决问题，就不需要考虑调用顺序，直接考虑结果，把每一次操作都写成一系列嵌套的方法中，使代码高聚合，方便管理。</p>
<p>文／峥吖（简书作者）<br>原文链接：<a href="http://www.jianshu.com/p/87ef6720a096" target="_blank" rel="external">http://www.jianshu.com/p/87ef6720a096</a><br>著作权归作者所有，转载请联系作者获得授权，并标注“简书作者”。</p>
<h2 id="5-如何导入ReactiveCocoa框架"><a href="#5-如何导入ReactiveCocoa框架" class="headerlink" title="5.如何导入ReactiveCocoa框架"></a>5.如何导入ReactiveCocoa框架</h2><p>通常都会使用CocoaPods（用于管理第三方框架的插件）帮助我们导入。</p>
<p>PS:CocoaPods教程</p>
<p>注意：</p>
<ul>
<li>podfile如果只描述pod ‘ReactiveCocoa’, ‘~&gt; 4.0.2-alpha-1’，会导入不成功。</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-e684a824ac51a74c.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<ul>
<li>报错提示信息</li>
</ul>
<p><img src="http://upload-images.jianshu.io/upload_images/304825-311191366a0b0972.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></p>
<ul>
<li>需要在podfile加上use_frameworks，重新pod install 才能导入成功。<br><img src="http://upload-images.jianshu.io/upload_images/304825-1e8effc941c8498a.png?imageMogr2/auto-orient/strip%7CimageView2/2" alt="Mou icon"></li>
</ul>
<h2 id="6-ReactiveCocoa常见类。"><a href="#6-ReactiveCocoa常见类。" class="headerlink" title="6.ReactiveCocoa常见类。"></a>6.ReactiveCocoa常见类。</h2><p>学习框架首要之处:个人认为先要搞清楚框架中常用的类，在RAC中最核心的类RACSiganl,搞定这个类就能用ReactiveCocoa开发了。</p>
<p>6.1RACSiganl:信号类,一般表示将来有数据传递，只要有数据改变，信号内部接收到数据，就会马上发出数据。</p>
<p>注意：</p>
<ul>
<li>信号类(RACSiganl)，只是表示当数据改变时，信号内部会发出数据，它本身不具备发送信号的能力，而是交给内部一个订阅者去发出。</li>
<li>默认一个信号都是冷信号，也就是值改变了，也不会触发，只有订阅了这个信号，这个信号才会变为热信号，值改变了才会触发。</li>
<li>如何订阅信号：调用信号RACSignal的subscribeNext就能订阅。</li>
<li><p>RACSiganl简单使用:</p>
<pre><code> // RACSignal使用步骤：
// 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id&lt;RACSubscriber&gt; subscriber))didSubscribe
// 2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.发送信号 - (void)sendNext:(id)value
</code></pre></li>
</ul>
<pre><code>// RACSignal底层实现：
// 1.创建信号，首先把didSubscribe保存到信号中，还不会触发。
// 2.当信号被订阅，也就是调用signal的subscribeNext:nextBlock
// 2.2 subscribeNext内部会创建订阅者subscriber，并且把nextBlock保存到subscriber中。
// 2.1 subscribeNext内部会调用siganl的didSubscribe
// 3.siganl的didSubscribe中调用[subscriber sendNext:@1];
// 3.1 sendNext底层其实就是执行subscriber的nextBlock

// 1.创建信号
RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {

// block调用时刻：每当有订阅者订阅信号，就会调用block。

// 2.发送信号
[subscriber sendNext:@1];

// 如果不在发送数据，最好发送信号完成，内部会自动调用[RACDisposable disposable]取消订阅信号。
[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

    // block调用时刻：当信号发送完成或者发送错误，就会自动执行这个block,取消订阅信号。

    // 执行完Block后，当前信号就不在被订阅了。

    NSLog(@&quot;信号被销毁&quot;);

}];
}];

// 3.订阅信号,才会激活信号.
[siganl subscribeNext:^(id x) {
// block调用时刻：每当有信号发出数据，就会调用block.
NSLog(@&quot;接收到数据:%@&quot;,x);
}];
</code></pre><p>6.2 RACSubscriber:表示订阅者的意思，用于发送信号，这是一个协议，不是一个类，只要遵守这个协议，并且实现方法才能成为订阅者。通过create创建的信号，都有一个订阅者，帮助他发送数据。</p>
<p>6.3 RACDisposable:用于取消订阅或者清理资源，当信号发送完成或者发送错误的时候，就会自动触发它。</p>
<ul>
<li>使用场景:不想监听某个信号时，可以通过它主动取消订阅信号。</li>
</ul>
<p>6.4 RACSubject:RACSubject:信号提供者，自己可以充当信号，又能发送信号。</p>
<ul>
<li>使用场景:通常用来代替代理，有了它，就不必要定义代理了。<br>RACReplaySubject:重复提供信号类，RACSubject的子类。</li>
<li>RACReplaySubject与RACSubject区别:<br>RACReplaySubject可以先发送信号，在订阅信号，RACSubject就不可以。</li>
<li>使用场景一:如果一个信号每被订阅一次，就需要把之前的值重复发送一遍，使用重复提供信号类。</li>
<li>使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。</li>
<li><p>RACSubject和RACReplaySubject简单使用:</p>
<pre><code> // RACSubject使用步骤
// 1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
// 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.发送信号 sendNext:(id)value

// RACSubject:底层实现和RACSignal不一样。
// 1.调用subscribeNext订阅信号，只是把订阅者保存起来，并且订阅者的nextBlock已经赋值了。
// 2.调用sendNext发送信号，遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock。

// 1.创建信号
RACSubject *subject = [RACSubject subject];

// 2.订阅信号
[subject subscribeNext:^(id x) {
// block调用时刻：当信号发出新值，就会调用.
NSLog(@&quot;第一个订阅者%@&quot;,x);
}];
[subject subscribeNext:^(id x) {
// block调用时刻：当信号发出新值，就会调用.
NSLog(@&quot;第二个订阅者%@&quot;,x);
}];

// 3.发送信号
[subject sendNext:@&quot;1&quot;];
</code></pre></li>
</ul>
<pre><code>// RACReplaySubject使用步骤:
// 1.创建信号 [RACSubject subject]，跟RACSiganl不一样，创建信号时没有block。
// 2.可以先订阅信号，也可以先发送信号。
// 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 2.2 发送信号 sendNext:(id)value

// RACReplaySubject:底层实现和RACSubject不一样。
// 1.调用sendNext发送信号，把值保存起来，然后遍历刚刚保存的所有订阅者，一个一个调用订阅者的nextBlock。
// 2.调用subscribeNext订阅信号，遍历保存的所有值，一个一个调用订阅者的nextBlock

// 如果想当一个信号被订阅，就重复播放之前所有值，需要先发送信号，在订阅信号。
// 也就是先保存值，在订阅值。

// 1.创建信号
RACReplaySubject *replaySubject = [RACReplaySubject subject];

// 2.发送信号
[replaySubject sendNext:@1];
[replaySubject sendNext:@2];

// 3.订阅信号
[replaySubject subscribeNext:^(id x) {

NSLog(@&quot;第一个订阅者接收到的数据%@&quot;,x);
}];

// 订阅信号
[replaySubject subscribeNext:^(id x) {

NSLog(@&quot;第二个订阅者接收到的数据%@&quot;,x);
}];
</code></pre><ul>
<li><p>RACSubject替换代理</p>
<pre><code>// 需求:
// 1.给当前控制器添加一个按钮，modal到另一个控制器界面
// 2.另一个控制器view中有个按钮，点击按钮，通知当前控制器

步骤一：在第二个控制器.h，添加一个RACSubject代替代理。
@interface TwoViewController : UIViewController

@property (nonatomic, strong) RACSubject *delegateSignal;

@end

步骤二：监听第二个控制器按钮点击
@implementation TwoViewController
- (IBAction)notice:(id)sender {
// 通知第一个控制器，告诉它，按钮被点了

// 通知代理
// 判断代理信号是否有值
if (self.delegateSignal) {
// 有值，才需要通知
[self.delegateSignal sendNext:nil];
}
}
@end

步骤三：在第一个控制器中，监听跳转按钮，给第二个控制器的代理信号赋值，并且监听.
@implementation OneViewController 
- (IBAction)btnClick:(id)sender {

// 创建第二个控制器
TwoViewController *twoVc = [[TwoViewController alloc] init];

// 设置代理信号
twoVc.delegateSignal = [RACSubject subject];

// 订阅代理信号
[twoVc.delegateSignal subscribeNext:^(id x) {

NSLog(@&quot;点击了通知按钮&quot;);
}];

// 跳转到第二个控制器
[self presentViewController:twoVc animated:YES completion:nil];

}
@end
</code></pre></li>
</ul>
<p>6.6RACTuple:元组类,类似NSArray,用来包装值.</p>
<p>6.7RACSequence:RAC中的集合类，用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。</p>
<p>使用场景：1.字典转模型</p>
<ul>
<li><p>RACSequence和RACTuple简单使用</p>
<pre><code> // 1.遍历数组
 NSArray *numbers = @[@1,@2,@3,@4];

// 这里其实是三步
// 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
// 第三步: 订阅信号，激活信号，会自动把集合中的所有值，遍历出来。
[numbers.rac_sequence.signal subscribeNext:^(id x) {

NSLog(@&quot;%@&quot;,x);
}];
</code></pre></li>
</ul>
<pre><code>// 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
NSDictionary *dict = @{@&quot;name&quot;:@&quot;xmg&quot;,@&quot;age&quot;:@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {

// 解包元组，会把元组的值，按顺序给参数里面的变量赋值
RACTupleUnpack(NSString *key,NSString *value) = x;

// 相当于以下写法
//        NSString *key = x[0];
//        NSString *value = x[1];

NSLog(@&quot;%@ %@&quot;,key,value);

}];


// 3.字典转模型
// 3.1 OC写法
NSString *filePath = [[NSBundle mainBundle] pathForResource:@&quot;flags.plist&quot; ofType:nil];

NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

NSMutableArray *items = [NSMutableArray array];

for (NSDictionary *dict in dictArr) {
FlagItem *item = [FlagItem flagWithDict:dict];
[items addObject:item];
}

// 3.2 RAC写法
NSString *filePath = [[NSBundle mainBundle] pathForResource:@&quot;flags.plist&quot; ofType:nil];

NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

NSMutableArray *flags = [NSMutableArray array];

_flags = flags;

// rac_sequence注意点：调用subscribeNext，并不会马上执行nextBlock，而是会等一会。
[dictArr.rac_sequence.signal subscribeNext:^(id x) {
// 运用RAC遍历字典，x：字典

FlagItem *item = [FlagItem flagWithDict:x];

[flags addObject:item];

}];

NSLog(@&quot;%@&quot;,  NSStringFromCGRect([UIScreen mainScreen].bounds));


// 3.3 RAC高级写法:
NSString *filePath = [[NSBundle mainBundle] pathForResource:@&quot;flags.plist&quot; ofType:nil];

NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
// map:映射的意思，目的：把原始值value映射成一个新值
// array: 把集合转换成数组
// 底层实现：当信号被订阅，会遍历集合中的原始值，映射成新值，并且保存到新的数组里。
NSArray *flags = [[dictArr.rac_sequence map:^id(id value) {

return [FlagItem flagWithDict:value];

}] array];
</code></pre><p>6.8RACCommand:RAC中用于处理事件的类，可以把事件如何处理,事件中的数据如何传递，包装到这个类中，他可以很方便的监控事件的执行过程。</p>
<p>使用场景:监听按钮点击，网络请求</p>
<ul>
<li><p>RACCommand简单使用</p>
<pre><code>// 一、RACCommand使用步骤:
// 1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
// 2.在signalBlock中，创建RACSignal，并且作为signalBlock的返回值
// 3.执行命令 - (RACSignal *)execute:(id)input

// 二、RACCommand使用注意:
// 1.signalBlock必须要返回一个信号，不能传nil.
// 2.如果不想要传递信号，直接创建空的信号[RACSignal empty];
// 3.RACCommand中信号如果数据传递完，必须调用[subscriber sendCompleted]，这时命令才会执行完毕，否则永远处于执行中。
// 4.RACCommand需要被强引用，否则接收不到RACCommand中的信号，因此RACCommand中的信号是延迟发送的。

// 三、RACCommand设计思想：内部signalBlock为什么要返回一个信号，这个信号有什么用。
// 1.在RAC开发中，通常会把网络请求封装到RACCommand，直接执行某个RACCommand就能发送请求。
// 2.当RACCommand内部请求到数据的时候，需要把请求的数据传递给外界，这时候就需要通过signalBlock返回的信号传递了。

// 四、如何拿到RACCommand中返回信号发出的数据。
// 1.RACCommand有个执行信号源executionSignals，这个是signal of signals(信号的信号),意思是信号发出的数据是信号，不是普通的类型。
// 2.订阅executionSignals就能拿到RACCommand中返回的信号，然后订阅signalBlock返回的信号，就能获取发出的值。

// 五、监听当前命令是否正在执行executing

// 六、使用场景,监听按钮点击，网络请求
</code></pre></li>
</ul>
<pre><code> // 1.创建命令
 RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {


NSLog(@&quot;执行命令&quot;);

// 创建空信号,必须返回信号
//        return [RACSignal empty];

// 2.创建信号,用来传递数据
return [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {

    [subscriber sendNext:@&quot;请求数据&quot;];

    // 注意：数据传递完，最好调用sendCompleted，这时命令才执行完毕。
    [subscriber sendCompleted];

    return nil;
}];

}];

 // 强引用命令，不要被销毁，否则接收不到数据
_conmmand = command;



 // 3.订阅RACCommand中的信号
[command.executionSignals subscribeNext:^(id x) {

[x subscribeNext:^(id x) {

    NSLog(@&quot;%@&quot;,x);
}];

}];

// RAC高级用法
// switchToLatest:用于signal of signals，获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号
[command.executionSignals.switchToLatest subscribeNext:^(id x) {

NSLog(@&quot;%@&quot;,x);
}];

// 4.监听命令是否执行完毕,默认会来一次，可以直接跳过，skip表示跳过第一次信号。
[[command.executing skip:1] subscribeNext:^(id x) {

if ([x boolValue] == YES) {
    // 正在执行
    NSLog(@&quot;正在执行&quot;);

}else{
    // 执行完成
    NSLog(@&quot;执行完成&quot;);
}

}];
// 5.执行命令
[self.conmmand execute:@1];
</code></pre><p> 6.9 RACMulticastConnection:用于当一个信号，被多次订阅时，为了保证创建信号时，避免多次调用创建信号中的block，造成副作用，可以使用这个类处理。</p>
<p> 使用注意:RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建.</p>
<ul>
<li><p>RACMulticastConnection简单使用:</p>
<pre><code> // RACMulticastConnection使用步骤:
 // 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id&lt;RACSubscriber&gt; subscriber))didSubscribe
 // 2.创建连接 RACMulticastConnection *connect = [signal publish];
 // 3.订阅信号,注意：订阅的不在是之前的信号，而是连接的信号。 [connect.signal subscribeNext:nextBlock]
// 4.连接 [connect connect]

// RACMulticastConnection底层原理:
// 1.创建connect，connect.sourceSignal -&gt; RACSignal(原始信号)  connect.signal -&gt; RACSubject
// 2.订阅connect.signal，会调用RACSubject的subscribeNext，创建订阅者，而且把订阅者保存起来，不会执行block。
// 3.[connect connect]内部会订阅RACSignal(原始信号)，并且订阅者是RACSubject
// 3.1.订阅原始信号，就会调用原始信号中的didSubscribe
// 3.2 didSubscribe，拿到订阅者调用sendNext，其实是调用RACSubject的sendNext
// 4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。
// 4.1 因为刚刚第二步，都是在订阅RACSubject，因此会拿到第二步所有的订阅者，调用他们的nextBlock
</code></pre></li>
</ul>
<pre><code>// 需求：假设在一个信号中发送请求，每次订阅一次都会发送请求，这样就会导致多次请求。
// 解决：使用RACMulticastConnection就能解决.

// 1.创建请求信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {


NSLog(@&quot;发送请求&quot;);

return nil;
}];
// 2.订阅信号
[signal subscribeNext:^(id x) {

NSLog(@&quot;接收数据&quot;);

}];
 // 2.订阅信号
[signal subscribeNext:^(id x) {

NSLog(@&quot;接收数据&quot;);

}];

// 3.运行结果，会执行两遍发送请求，也就是每次订阅都会发送一次请求


// RACMulticastConnection:解决重复请求问题
// 1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {


NSLog(@&quot;发送请求&quot;);
[subscriber sendNext:@1];

return nil;
}];

 // 2.创建连接
RACMulticastConnection *connect = [signal publish];

 // 3.订阅信号，
 // 注意：订阅信号，也不能激活信号，只是保存订阅者到数组，必须通过连接,当调用连接，就会一次性调用所有订阅者的sendNext:
[connect.signal subscribeNext:^(id x) {

NSLog(@&quot;订阅者一信号&quot;);

}];

[connect.signal subscribeNext:^(id x) {

NSLog(@&quot;订阅者二信号&quot;);

}];

// 4.连接,激活信号
[connect connect];
</code></pre><p>6.10 RACScheduler:RAC中的队列，用GCD封装的。</p>
<p>6.11 RACUnit :表⽰stream不包含有意义的值,也就是看到这个，可以直接理解为nil.</p>
<p>6.12 RACEvent: 把数据包装成信号事件(signal event)。它主要通过RACSignal的-materialize来使用，然并卵。</p>
<p>文／峥吖（简书作者）<br>原文链接：<a href="http://www.jianshu.com/p/87ef6720a096" target="_blank" rel="external">http://www.jianshu.com/p/87ef6720a096</a><br>著作权归作者所有，转载请联系作者获得授权，并标注“简书作者”。</p>
<h2 id="7-ReactiveCocoa开发中常见用法。"><a href="#7-ReactiveCocoa开发中常见用法。" class="headerlink" title="7.ReactiveCocoa开发中常见用法。"></a>7.ReactiveCocoa开发中常见用法。</h2><p>7.1 代替代理:</p>
<ul>
<li>rac_signalForSelector：用于替代代理。</li>
</ul>
<p>7.2 代替KVO :</p>
<ul>
<li>rac_valuesAndChangesForKeyPath：用于监听某个对象的属性改变。</li>
</ul>
<p>7.3 监听事件:</p>
<ul>
<li>rac_signalForControlEvents：用于监听某个事件。</li>
</ul>
<p>7.4 代替通知:</p>
<ul>
<li>rac_addObserverForName:用于监听某个通知。</li>
</ul>
<p>7.5 监听文本框文字改变:</p>
<ul>
<li>rac_textSignal:只要文本框发出改变就会发出这个信号。</li>
</ul>
<p>7.6 处理当界面有多次请求时，需要都获取到数据时，才能展示界面</p>
<ul>
<li>rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组)，每一个signal都至少sendNext过一次，就会去触发第一个selector参数的方法。</li>
<li>使用注意：几个信号，参数一的方法就几个参数，每个参数对应信号发出的数据。</li>
</ul>
<p>7.7 代码演示</p>
<pre><code>     // 1.代替代理
// 需求：自定义redView,监听红色view中按钮点击
// 之前都是需要通过代理监听，给红色View添加一个代理属性，点击按钮的时候，通知代理做事情
// rac_signalForSelector:把调用某个对象的方法的信息转换成信号，就要调用这个方法，就会发送信号。
// 这里表示只要redV调用btnClick:,就会发出信号，订阅就好了。
[[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
    NSLog(@&quot;点击红色按钮&quot;);
}];

// 2.KVO
// 把监听redV的center属性改变转换成信号，只要值改变就会发送信号
// observer:可以传入nil
[[redV rac_valuesAndChangesForKeyPath:@&quot;center&quot; options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {

    NSLog(@&quot;%@&quot;,x);

}];

// 3.监听事件
// 把按钮点击事件转换为信号，点击按钮，就会发送信号
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {

    NSLog(@&quot;按钮被点击了&quot;);
}];

// 4.代替通知
// 把监听到的通知转换信号
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
    NSLog(@&quot;键盘弹出&quot;);
}];

// 5.监听文本框的文字改变
[_textField.rac_textSignal subscribeNext:^(id x) {

   NSLog(@&quot;文字改变了%@&quot;,x);
 }];

 // 6.处理多个请求，都返回结果的时候，统一做处理.
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {

    // 发送请求1
    [subscriber sendNext:@&quot;发送请求1&quot;];
    return nil;
}];

RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id&lt;RACSubscriber&gt; subscriber) {
    // 发送请求2
    [subscriber sendNext:@&quot;发送请求2&quot;];
    return nil;
}];

// 使用注意：几个信号，参数一的方法就几个参数，每个参数对应信号发出的数据。
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];


}
// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
NSLog(@&quot;更新UI%@  %@&quot;,data,data1);
}
</code></pre><h2 id="8-ReactiveCocoa常见宏。"><a href="#8-ReactiveCocoa常见宏。" class="headerlink" title="8.ReactiveCocoa常见宏。"></a>8.ReactiveCocoa常见宏。</h2><p> 8.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。</p>
<pre><code> // 只要文本框文字改变，就会修改label的文字
RAC(self.labelView,text) = _textField.rac_textSignal;
</code></pre><p> 8.2 RACObserve(self, name):监听某个对象的某个属性,返回的是信号。</p>
<pre><code>  [RACObserve(self.view, center) subscribeNext:^(id x) {

    NSLog(@&quot;%@&quot;,x);
}];
</code></pre><p>8.3  @weakify(Obj)和@strongify(Obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入，需要自己手动导入，RACEXTScope.h才可以使用。但是每次导入都非常麻烦，只需要在主头文件自己导入就好了。</p>
<p>8.4 RACTuplePack：把数据包装成RACTuple（元组类）</p>
<pre><code>// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@10,@20);
</code></pre><p>8.5 RACTupleUnpack：把RACTuple（元组类）解包成对应的数据。</p>
<pre><code>    // 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@&quot;xmg&quot;,@20);

// 解包元组，会把元组的值，按顺序给参数里面的变量赋值
// name = @&quot;xmg&quot; age = @20
RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
</code></pre><p> <a href="http://www.tuicool.com/articles/uUfyMf" target="_blank" rel="external">如何建立基于CocoaPods的ReactiveCocoa工程</a></p>

      
    </div>
    
  </div>
  
    
<nav id="article-nav">
  
    <a href="/2015/05/15/创建CoreData基本教程/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption"><</strong>
      <div class="article-nav-title">
        
          创建CoreData基本教程
        
      </div>
    </a>
  
  
    <a href="/2015/02/19/从零开始开发一款Android-App/" id="article-nav-older" class="article-nav-link-wrap">
      <div class="article-nav-title">从零开始开发一款Android App</div>
      <strong class="article-nav-caption">></strong>
    </a>
  
</nav>

  
</article>


<div class="share_jia">
	<!-- JiaThis Button BEGIN -->
	<div class="jiathis_style">
		<span class="jiathis_txt">Share to: &nbsp; </span>
		<a class="jiathis_button_facebook"></a> 
    <a class="jiathis_button_twitter"></a>
    <a class="jiathis_button_plus"></a> 
    <a class="jiathis_button_tsina"></a>
		<a class="jiathis_button_cqq"></a>
		<a class="jiathis_button_douban"></a>
		<a class="jiathis_button_weixin"></a>
		<a class="jiathis_button_tumblr"></a>
    <a href="http://www.jiathis.com/share" class="jiathis jiathis_txt jtico jtico_jiathis" target="_blank"></a>
	</div>
	<script type="text/javascript" src="http://v3.jiathis.com/code/jia.js?uid=1405949716054953" charset="utf-8"></script>
	<!-- JiaThis Button END -->
</div>






<div class="duoshuo">
	<!-- 多说评论框 start -->
	<div class="ds-thread" data-thread-key="ReactiveCocoa之基础篇" data-title="ReactiveCocoa之基础篇" data-url="http://yoursite.com/2015/05/04/ReactiveCocoa之基础篇/"></div>
	<!-- 多说评论框 end -->
	<!-- 多说公共JS代码 start (一个网页只需插入一次) -->
	<script type="text/javascript">
	var duoshuoQuery = {short_name:"true"};
	(function() {
		var ds = document.createElement('script');
		ds.type = 'text/javascript';ds.async = true;
		ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
		ds.charset = 'UTF-8';
		(document.getElementsByTagName('head')[0] 
		 || document.getElementsByTagName('body')[0]).appendChild(ds);
	})();
	</script>
	<!-- 多说公共JS代码 end -->
</div>




</div>
      <footer id="footer">
  <div class="outer">
    <div id="footer-info">
    	<div class="footer-left">
    		&copy; 2019 Grx
    	</div>
      	<div class="footer-right">
      		<a href="http://hexo.io/" target="_blank">Hexo</a>  Theme <a href="https://github.com/litten/hexo-theme-yilia" target="_blank">Yilia</a> by Litten
      	</div>
    </div>
  </div>
</footer>
    </div>
    
  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">


<script>
	var yiliaConfig = {
		fancybox: true,
		mathjax: true,
		animate: true,
		isHome: false,
		isPost: true,
		isArchive: false,
		isTag: false,
		isCategory: false,
		open_in_new: false
	}
</script>
<script src="http://7.url.cn/edu/jslib/comb/require-2.1.6,jquery-1.9.1.min.js"></script>
<script src="/js/main.js"></script>






<script type="text/x-mathjax-config">
MathJax.Hub.Config({
    tex2jax: {
        inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
        processEscapes: true,
        skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
    }
});

MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
        all[i].SourceElement().parentNode.className += ' has-jax';                 
    }       
});
</script>

<script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script>


  </div>
</body>
</html>